package com.hd123.rumba.gwt.entrypoint2.client;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.DeckPanel;
import com.google.gwt.user.client.ui.HasCaption;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.hd123.rumba.gwt.base.client.Session;
import com.hd123.rumba.gwt.base.client.exception.ClientBizException;
import com.hd123.rumba.gwt.base.client.util.GWTUtil;
import com.hd123.rumba.gwt.util.client.IsContentPanel;
import com.hd123.rumba.gwt.util.client.JumpParams;
import com.hd123.rumba.gwt.util.client.Util;
import com.hd123.rumba.gwt.widget2.client.panel.RTitleItem;
import com.hd123.rumba.gwt.widget2.client.panel.RTitlePanel;

/**
 * 界面模块框架，设计作为大部分具体模块的基类。
 * <p>
 * 
 * 设计具备以下功能：
 * <ol>
 * <li>是一个EnterPoint。
 * <li>提供基础的界面布局BorderLayout。
 * <li>提供标准的标题部件TitlePanel，位于north。
 * <li>提供模块内页面切换功能。
 * </ol>
 * 
 * @author LiQi
 * 
 */
public abstract class EPTitleModule2 extends EntryPointBase2 {

  public static final String STARTNODE_NAME = "start";
  public static final String STARTNODE_HOME = "home";
  private static final String PN_JUMPPARAMS_ID = "_jpid_";

  /** 整个模块的基础面板 */
  private VerticalPanel root;

  /** 标题面板 */
  private RTitlePanel titlePanel;

  /** 工作区域的基础面板 */
  private DeckPanel workingArea;

  /** 模块首页标题项 */
  private RTitleItem titleItemHome;

  /** 当前面板的startNode */
  private String currentStartNode;

  /** startNode到内容面板的映射表，其中存放必须是IsContentPanel */
  private Map<String, IsContentPanel> contentPanelMap;

  /** 内容面板顺序表 */
  private List contentPanels;

  /** 是否启用History监听器 */
  private boolean historyListenerEnabled = true;

  public EPTitleModule2() {
    super();
    contentPanelMap = new HashMap();
    contentPanels = new ArrayList();
  }

  protected void onSessionLoad() {
    super.onSessionLoad();

    History.addValueChangeHandler(new ModuleHistoryHandler());
    Window.setTitle(getModuleCaption());
    buildUI();
    onShow();
    dispatch(getParameters());
  }

  private void buildUI() {
    root = new VerticalPanel();
    root.setWidth("100%");
    root.setHeight("100%");

    // 标题面板
    titlePanel = new RTitlePanel();
    titlePanel.setIconUrl(GWTUtil.getStaticContentURL(getModuleIcon()));
    titlePanel.clearTitles();
    titleItemHome = new RTitleItem(getModuleCaption(), STARTNODE_NAME + "=" + STARTNODE_HOME);
    titlePanel.pushTitle(titleItemHome);
    root.add(titlePanel);

    // 工作区域
    workingArea = new DeckPanel();
    workingArea.setWidth("100%");
    workingArea.setHeight("100%");
    root.add(workingArea);
    root.setCellHeight(workingArea, "100%");

    RootPanel.get().add(root);
  }

  /**
   * 当模块显示后被调用。
   * 
   */
  protected void onShow() {

  }

  /**
   * 模块标题面板上的图标
   * 
   * @return
   */
  protected abstract String getModuleIcon();

  /**
   * 整个模块的基础面板
   * 
   * @return
   */
  public VerticalPanel getRootPanel() {
    return root;
  }

  /**
   * 标题面板
   * 
   * @return
   */
  public RTitlePanel getTitlePanel() {
    return titlePanel;
  }

  /**
   * 根据当前模块标题重置标题栏
   * 
   */
  public void resetTitlePanel() {
    IsContentPanel currentPanel = contentPanelMap.get(currentStartNode);
    if (currentPanel != null && currentPanel instanceof HasCaption) {
      titlePanel.clearTitles();
      titlePanel.pushTitle(titleItemHome);
      titlePanel.pushTitle(new RTitleItem(((HasCaption) currentPanel).getCaption()));
    }
  }

  /**
   * 工作区域的基础面板
   * 
   * @return
   */
  public DeckPanel getWorkingAreaPanel() {
    return workingArea;
  }

  /**
   * 模块首页标题项
   * 
   * @return
   */
  public RTitleItem getTitleItemHome() {
    return titleItemHome;
  }

  /**
   * 当前面板的startNode
   * 
   * @return
   */
  public String getCurrentStartNode() {
    return currentStartNode;
  }

  /**
   * 当前的功能视图。从Session中取得，调用者需要确保Servlet中已经将功能视图信息放入到Session中，<br>
   * key = Session.KEY_CFVPREFIX。否则将返回null。
   * 
   * @return
   */
  public String getCfvPrefix() {
    return (String) this.getCurrentSession().get(Session.KEY_CFVPREFIX);
  }

  /**
   * 持久化配置。从Session中取得，调用者需要确保Servlet中已经将持久化配置信息放入到Session中，<br>
   * key = Session.KEY_PERZCONFIG。否则将返回null。
   * 
   * @return
   */
  /*-OTTER 不使用
  public BPerzConfig getPerzConfig() {
    return (BPerzConfig) this.getCurrentSession().get(Session.KEY_PERZCONFIG);
  }*/

  /**
   * 取得指定的内容面板。
   * 
   * @param startNode
   * @return
   */
  public IsContentPanel getContentPanel(String startNode) {
    return contentPanelMap.get(startNode);
  }

  /**
   * 取得当前所有的内容面板。
   * 
   * @return
   */
  public Collection<IsContentPanel> getContentPanels() {
    return contentPanelMap.values();
  }

  /**
   * 启用History监听器。<br>
   * 如果不启用History监听器，则不会自动进行dispatch操作。
   */
  protected void enableHistoryListener() {
    historyListenerEnabled = true;
  }

  /**
   * 禁用History监听器。<br>
   * 如果禁用History监听器，则不会自动进行dispatch操作。
   */
  protected void disableHistoryListener() {
    historyListenerEnabled = false;
  }

  /**
   * 根据参数分发请求
   * 
   * @param params
   */
  protected abstract void dispatch(Map params);

  /**
   * 显示指定的内容面板。
   * 
   * @param panel
   *            指定被显示的内容面板。
   * @param params
   *            指定参数。
   * @throws ClientBizException
   */
  public void showContentPanel(IsContentPanel panel, Map params) throws ClientBizException {
    assert panel != null;
    assert params != null;

    // 取得或构造JumpParams
    JumpParams jparams = null;
    String jpid = (String) params.get(PN_JUMPPARAMS_ID);
    if (jpid != null) {
      jparams = JumpParamsBuffer.poll(jpid);
      params.remove(PN_JUMPPARAMS_ID);
    }
    if (jparams == null)
      jparams = new JumpParams();
    jparams.getUrlParams().putAll(params);

    // 检查权限
    String permCode = panel.getEntryPermission();
    if (!hasPermission(permCode, true))
      throw ClientBizException.noPermission(permCode);

    // 调用进入前的处理
    panel.beforeShowPanel(jparams);

    // 隐藏旧面板
    IsContentPanel oldPanel = contentPanelMap.get(currentStartNode);
    if (oldPanel != null) {
      oldPanel.onHidePanel();
    }

    // 显示新的面板
    int index; // 在workingArea中的顺序
    if (!contentPanelMap.containsKey(panel.getStartNode())) {
      index = contentPanels.size();
      contentPanelMap.put(panel.getStartNode(), panel);
      contentPanels.add(panel);
      workingArea.add((Widget) panel);
    } else {
      index = contentPanels.indexOf(panel);
    }
    workingArea.showWidget(index);
    panel.onShowPanel(jparams);
    currentStartNode = panel.getStartNode();

    // 刷新标题
    resetTitlePanel();
  }

  /**
   * 模块内跳转，除startNode以外，没有其它参数
   * 
   * @param startNode
   */
  public void jump(String startNode) {
    jump(startNode, null);
  }

  /**
   * 模块内跳转
   * 
   * @param startNode
   * @param params
   *            格式为：param1=aaa&param2=bbb
   */
  public void jump(String startNode, String params) {
    String url = STARTNODE_NAME + "=" + startNode;
    if (params != null)
      url += "&" + params;
    History.newItem(url);
  }

  /**
   * 提供模块内进行内容面板跳转的功能。
   * 
   * @param params
   */
  public void jump(JumpParams params) {
    String id = JumpParamsBuffer.offer(params);

    StringBuffer sbUrl = new StringBuffer();
    sbUrl.append(PN_JUMPPARAMS_ID + "=" + id);
    Object[] keys = params.getUrlParams().keySet().toArray();
    for (int i = 0, n = keys.length; i < n; i++) {
      String key = (String) keys[i];
      sbUrl.append("&");
      sbUrl.append(key + "=" + params.getUrlParam(key));
    }

    History.newItem(sbUrl.toString());
  }

  /**
   * History事件处理
   * 
   * @author LiQi
   * 
   */
  private class ModuleHistoryHandler implements ValueChangeHandler<String> {

    public void onValueChange(ValueChangeEvent<String> event) {
      if (historyListenerEnabled) {
        HashMap params = Util.getParameters(event.getValue());
        dispatch(params);
      }
    }
  }

  /**
   * 跳转参数缓冲，用于在参数中传对象
   * 
   * @author LiQi
   * 
   */
  private static class JumpParamsBuffer {

    private static int nextId = 0;

    private static HashMap<String, Serializable> buffer = new HashMap();

    /**
     * 向缓冲中放入一个参数。
     * 
     * @param params
     * @return 返回重新取得参数的唯一标识。
     */
    public static String offer(JumpParams params) {
      String id = String.valueOf(nextId);
      nextId++;
      buffer.put(id, params);
      return id;
    }

    /**
     * 从缓冲中取出参数，取出后缓冲中将不再包含取出的参数。
     * 
     * @param id
     *            指定参数的唯一标识。
     * @return 返回取出的参数，若找不到将返回null。
     */
    public static JumpParams poll(String id) {
      JumpParams params = (JumpParams) buffer.get(id);
      buffer.remove(id);
      return params;
    }
  }

}
